iT邦幫忙

2024 iThome 鐵人賽

DAY 14
0

Takeaway

  • Context可將Props直送到所要的組件
  • 使用Context的三步驟:創建 → 使用 → 提供
  • 譬如<LevelContext.Provider value={level + 1}>可以讓包起來的value值遞增
  • createContext的參數只有一個
  • Context類似CSS屬性,適合主題切換、讀帳戶資訊、做為路由、狀態管理
  • 雖然好用,但傳遞Props和JSX還是優先選項,更能看出資訊流向
  • 舊版React還要定義contextTypes
  • Reducer結合Context:將tasks和dispatch放進兩個Context裡傳遞
  • 使用組件導出Reducer結合Context:可簡化app.js的架構
  • Reducer + Context和Redux的載入速度差不多
  • createContext也要引入!
  • Reducer函數在程式碼尾段引入

Edit Context

再複習一遍:Reducer是把「狀態更新的邏輯」集中起來管理,Context是讓組件更容易取值。

話不多說,開始實作Reducer + Context,先放添加鈕。確認連好「App.js」和「Add.js」後,我們在「Add.js」引入useState。

// Add.js

// 引入useState
const [text, setText] = useState("");
return (
<>
  <input
    placeholder="添加待辦"
    value={text}
    onChange={(e) => setText(e.target.value)}
  />
  <button
    onClick={() => {
      setText("");
    }}
  >
    添加
  </button>
</>
);

現在的狀態很簡單,還只是個按下添加後會清空輸入欄的按鈕。

由於Reducer和Context最後會集中在一處,這時要到「Logic.js」引入createContext, useContext, useReducer。我們首先在「Logic.js」裡設置Reducer函數tasksReducer和初始資料initialTasks。這兩個接下來會被Context的Provider設為要傳遞的東西。

// Logic.js

// 設定Reducer函數
function tasksReducer(tasks, action) {
  switch (action.type) {
    case "added": {
      return [
        ...tasks,
        {
          id: action.id,
          text: action.text,
          done: false,
        },
      ];
    }
    case "changed": {
      return tasks.map((t) => {
        if (t.id === action.task.id) {
          return action.task;
        } else {
          return t;
        }
      });
    }
    case "deleted": {
      return tasks.filter((t) => t.id !== action.id);
    }
    default: {
      throw Error("Unknown action: " + action.type);
    }
  }
}

// 設定初始狀態
const initialTasks = [
  { id: 0, text: "學習State", done: true },
  { id: 1, text: "學習Reducer", done: false },
  { id: 2, text: "學習Context", done: false },
];

設定Context的第一步是「創建」,亦即宣告Tasks和Dispatch是createContext(null)。這裡就像在宣告State和Setter是useState(0)一樣。

設定Context的第二步是「使用」。我們要用useContext把Tasks和Dispatch(這裡的Tasks和Dispatch是createContext時所創建的)包在組件裡傳出去。

設定Context的第三步是「提供」。所以我們加上TasksProvider,把Reducer函數和初始資料用task和dispatch宣告起來,然後在return時賦予給{ children }。

// Logic.js

// 「創建」Context
const Tasks = createContext(null);
const Dispatch = createContext(null);

// 「提供」Context
export function TasksProvider({ children }) {
  const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);

  return (
    <Tasks.Provider value={tasks}>
      <Dispatch.Provider value={dispatch}>{children}</Dispatch.Provider>
    </Tasks.Provider>
  );
}

// 「使用」Context
export function useTasks() {
  return useContext(Tasks);
}
export function useDispatch() {
  return useContext(Dispatch);
}

於是可以回到「Add.js」。當然要記得import { useDispatch } from "./Logic.js"。然後在組件裡const dispatch = useDispatch(),並派遣東西給按鈕。
這裡有個隱密的坑,那就是「App.js」也要記得改啊!從return <Add />改成

return (
    <TasksProvider>
        <h1>React待辦事項</h1>
        <Add />
    </TasksProvider>
);

不過現在還看不到加了什麼。所以最後來完成「List.js」吧。這邊就是用到各種State觀念了。
然後可以對照一下「List.js」和Reducer函數的code,可以更注意到Reducer是收到action之後,在不同type之間switch的。


最後附上初始資料傳遞路徑圖:





上一篇
【Day13】Reducer
下一篇
【Day15】ref
系列文
【現在學React還來得及嗎?】30天Takeaway分享30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言